const path = require('path');

/**
 我们期望yield后面跟一个promise的时候，这个promise成功的值会被赋值给yield操作符左边的变量

 实现异步代码像同步一样书写
 */
/*let fs = require('fs').promises;
function* read() {
  // try {
  let content = yield fs.readFile(path.resolve(__dirname, 'name.txt'), 'utf8');
  let age = yield fs.readFile(path.resolve(__dirname, content.trim()), 'utf8');
  return age //会作为最后一次it.next()返回值的.value
  // } catch (err) {
  //   console.log('*read 里的 try catch:', err);
  // }
  console.log('* read end');
}

let it = read();
let {value, done} = it.next(); //这里value是个promise

value.then(
  data => {
    let {value, done} = it.next(data);

    value.then(
      data => {
        let {value, done} = it.next(data); //最后一次next,中value即为函数的return
        console.log(value)
      }
      , err => {
        it.throw(err)
      }
    );
  }
  , err => {
    it.throw(err)
  } // 这里不会中断整个程序运行 因为promise实现里 then(onFufilled, onRejected), onFufilled和onRejected执行时 会被try catch包裹, 另外onRejected的话 会将.then返回的promise状态改为REJECTED
)*/
//通过上面↑这段代码我们生成器函数里的代码就能够实现其功能了:异步代码像同步一样书写
//但我们还期望上面这段"补丁代码"能帮我们自动生成


/** co 使用*/
/*let fs = require('fs').promises;

function* read() {
  try {
    let content = yield fs.readFile(path.resolve(__dirname, 'name.txt' + '123'), 'utf8');
    let age = yield fs.readFile(path.resolve(__dirname, content.trim()), 'utf8');
    return age //会作为最后一次it.next()返回值的.value
  } catch (err) {
    console.log('*read 里的 try catch:', err);
  }

  console.log('* read end');
}

let co = require('co');
co(read()).then(data => {
  console.log('co then success data结果:', data);
}, err => {
  console.log('co then reject');
  console.log(err);
});*/

/* 如果异步任务有抛错的话 输出如下
*read 里的 try catch: [Error: ENOENT: no such file or directory, open 'F:\js_workplace\tutorial\node_tutorial\3.generator\age.txt123'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'F:\\js_workplace\\tutorial\\node_tutorial\\3.generator\\age.txt123'
}
* read end
co then success data结果: undefined
*/


/** co 实现*/
// let fs = require('fs').promises;
const fs = require('fs');
const Promise = require('../2.promise/history4【各种方法】/promise')
fs.readFile2 = (...args)=>{
  return new Promise((resolve, reject) => {
    fs.readFile(...args,function(err,data){
      if(err) return reject(err);
      resolve(data);
    });
  });
}

function* read() {
  try {
    console.log('1');
    let content = yield fs.readFile2(path.resolve(__dirname, 'name.txt' + '123'), 'utf8');
    console.log('ff'); //← 不会走
    let age = yield fs.readFile2(path.resolve(__dirname, content.trim()), 'utf8');
    return age //会作为最后一次it.next()返回值的.value
  } catch (err) {
    console.log('2');
    console.log('*read 里的 try catch:', err);
  }

  console.log('3');
  console.log('* read end');
}

co(read()).then(data => {
  console.log('co then success data结果:', data); //4
}, err => {
  console.log('co then reject');
  console.log(err);
});

function co(it) {
  return new Promise((resolve, reject) => {
    // 异步的递归 next方法
    // 异步调用 我要等待第一个next 执行完后 在调用第二个
    function next(data) {
      try {
        console.log('it.next(data) data:', data);
        var {value, done} = it.next(data);
        console.log('value', value);
        console.log('done', done);
      } catch (e) {
        console.log('我是 co next 里的 try catch');
        reject(e);
      }

      console.log('等待 next 结果');

      if (done) {
        console.log('done');
        resolve(value); // 最终的结果抛出来了
      } else {
        //TODO 有可能yield age+100 后面是个普通值 而不是 promise
        // ，so直接Promise.resolve包装成promise
        Promise.resolve(value)/*←如果Promise.resolve的是一个promise 会等到这个被resolve的promise状态改变后,以它改变成的状态来改变自己,即Promise.resolve()后返回的那个promise(相当于就是做了一次转手,最终什么状态和结果都是看value的状态和结果)*/.then(
          data => {
            next(data);
          }
          , err => {
            console.log('it.throw(err)：', err);
            // 这里不会中断整个程序运行 因为promise实现里 then(onFufilled, onRejected), onFufilled和onRejected执行时 会被try catch包裹, 另外onRejected的话 会将.then返回的promise状态改为REJECTED
            it.throw(err); //todo 这里为什么会触发 `*read 里的 try catch`, 查看 1.generator.js case4、5 // 这里的it.throw 是放在 Promise.resolve(value) 返回的promiseX里的, 而这个promiseX的resolve和reject 又是被 value(即第一个it.next返回的promise对象) .then(resolve, reject) 的, 即promiseX的resolve和reject是放在value这个promise对象里的， 而value对象是从 * read 导出的。 也就是说 value状态改变, 调用promiseX的resolve或reject, 调用it.throw, 这个过程是由在生成器生成的对象value(promise)触发的, 而我们 1.generator.js case5 中知道, 这种情况下 相当于直接在生成器里 直接it.throw
            console.log('aaaa');
            resolve();
          }
        )
      }
    }

    next();
  })
}

/*
it.next(data) data: undefined
1
value Promise { <pending> }
done false
等待 next 结果
it.throw(err)： [Error: ENOENT: no such file or directory, open 'F:\js_workplace\tutorial\node_tutorial\3.generator\name.txt123'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'F:\\js_workplace\\tutorial\\node_tutorial\\3.generator\\name.txt123'
}
2
*read 里的 try catch: [Error: ENOENT: no such file or directory, open 'F:\js_workplace\tutorial\node_tutorial\3.generator\name.txt123'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'F:\\js_workplace\\tutorial\\node_tutorial\\3.generator\\name.txt123'
}
3
* read end
aaaa
co then success data结果: undefined

*/
